package com.xfcy.blog.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import com.xfcy.blog.common.constant.Constant;
import com.xfcy.blog.common.constant.SqlConstant;
import com.xfcy.blog.entity.Article;
import com.xfcy.blog.service.ElasticSearchService;
import com.xfcy.blog.strategy.impl.EsSearchStrategyImpl;
import com.xfcy.blog.utils.BeanCopyUtil;
import com.xfcy.blog.vo.ArticleSearchVO;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ElasticSearchService的实现类
 * @author 晓风残月Lx
 * @date 2023/4/24 19:54
 */
@Service("elasticSearchServiceImpl")
public class ElasticSearchServiceImpl implements ElasticSearchService {

    private static final Logger logger = LoggerFactory.getLogger(EsSearchStrategyImpl.class);

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 新增数据
     * @param article
     */
    @Override
    @Async("threadPoolTaskExecutor")
    public void addArticleSearchVO(Article article) {
        long time = System.currentTimeMillis();
        ArticleSearchVO articleSearchVO = BeanCopyUtil.copyObject(article, ArticleSearchVO.class);
        elasticsearchRestTemplate.save(articleSearchVO);
        logger.info("es新增文章,耗时:"+(System.currentTimeMillis() - time));
    }

    @Override
    @Async("threadPoolTaskExecutor")
    public void updateArticleSearchVO(Article article) {
        long time = System.currentTimeMillis();
        ArticleSearchVO articleSearchVO = BeanCopyUtil.copyObject(article, ArticleSearchVO.class);

        String obj = JSONObject.toJSONString(articleSearchVO);
        Document document = Document.parse(obj);

        UpdateQuery updateQuery = UpdateQuery.builder(String.valueOf(articleSearchVO.getId()))
                .withDocument(document).build();

        IndexCoordinates indexCoordinatesFor = elasticsearchRestTemplate.getIndexCoordinatesFor(ArticleSearchVO.class);

        elasticsearchRestTemplate.update(updateQuery, indexCoordinatesFor);
        logger.info("es修改文章内容,耗时:"+(System.currentTimeMillis() - time));
    }

    /**
     * 批量删除数据
     * @param ids
     */
    @Override
    @Async("threadPoolTaskExecutor")
    public void deleteBatch(List<Long> ids) {
        ids.forEach(id -> elasticsearchRestTemplate.delete(id.toString(), ArticleSearchVO.class));
    }


    /**
     * 根据关键词查简介和标题
     *
     * @param keywords
     * @return
     */
    public List<ArticleSearchVO> searchArticleByTitleAndSummary(String keywords) {
        if (StringUtils.isBlank(keywords)) {
            return new ArrayList<>();
        }
        return search(buildQuery(keywords));
    }

    /**
     * 搜索文章构造
     *
     * @param keywords
     * @return
     */
    private NativeSearchQueryBuilder buildQuery(String keywords) {
        // 条件构造器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 根据关键词搜索文章标题或内容
        boolQueryBuilder.must(QueryBuilders.boolQuery()
                        .should(QueryBuilders.matchQuery(SqlConstant.TITLE, keywords)))
                .should(QueryBuilders.matchQuery(SqlConstant.SUMMARY, keywords));
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        return nativeSearchQueryBuilder;
    }

    /**
     * 文章搜索结果高亮
     * @param nativeSearchQueryBuilder
     * @return
     */
    private List<ArticleSearchVO> search(NativeSearchQueryBuilder nativeSearchQueryBuilder) {
        // 添加文章标题高亮
        HighlightBuilder.Field titleField = new HighlightBuilder.Field(SqlConstant.TITLE);
        titleField.preTags(Constant.START_TAG);
        titleField.postTags(Constant.LAST_TAG);

        // 添加文章内容高亮
        HighlightBuilder.Field summaryField = new HighlightBuilder.Field(SqlConstant.SUMMARY);
        summaryField.preTags(Constant.START_TAG);
        summaryField.postTags(Constant.LAST_TAG);
        summaryField.fragmentSize(200);
        nativeSearchQueryBuilder.withHighlightFields(titleField, summaryField);

        try{
            SearchHits<ArticleSearchVO> search = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), ArticleSearchVO.class);
            return search.getSearchHits().stream().map(hit -> {
                ArticleSearchVO article = hit.getContent();
                // 获取文章标题高亮数据
                List<String> titleHighLightList = hit.getHighlightFields().get(SqlConstant.TITLE);
                if (CollectionUtils.isNotEmpty(titleHighLightList)) {
                    // 替换标题数据
                    article.setTitle(titleHighLightList.get(titleHighLightList.size() - 1));
                }
                // 获取文章简介高亮数据
                List<String> summaryHighLightList = hit.getHighlightFields().get(SqlConstant.SUMMARY);
                if (CollectionUtils.isNotEmpty(summaryHighLightList)) {
                    // 替换简介数据
                    article.setSummary(summaryHighLightList.get(summaryHighLightList.size() - 1));
                }
                return article;
            }).collect(Collectors.toList());
        }catch (Exception e) {
            logger.error(e.getMessage());
        }
        return new ArrayList<>();
    }

}
